#! /usr/bin/python3


import re
import time
from src.libs.logger import logger
from src.config import global_config
from src.community.openeuler_community_info_query import sig_info_query
from src.libs.csvrw import CSVRW
from src.libs.common_commands import Command


COMPARE_OPERATORS = [' > ', ' < ', ' = ', ' >= ', ' <= ']

class AnalyseBadPackage:
    """
    analyse bad pacakge's info and post a report
    """

    @classmethod
    def _add_missing_deps_owner_info(cls, deps_info:dict, influented_pkgs, analyse_report_details:dict):
        """
        unit sig owner info and bin, src info
        Args:
            deps_info:
            analyse_report_details:
        Returns:

        """

        logger.info("get deps_info for %s", influented_pkgs)
        for pkg in influented_pkgs:
            used_time = time.time()
            main_key = pkg + str(used_time)
            analyse_report_details[main_key] = dict()
            analyse_report_details[main_key]['Package'] = pkg
            analyse_report_details[main_key].update(deps_info)
            default_info = {
                        'Sig': 'NA', 
                        'Maintainer': "NA",
                        'Maintainer_email': 'NA',
                        'QA': 'NA',
                        'Team': 'NA', 
                        'Owner': 'NA', 
                        'Owner_email': 'NA'
                    }
            package_owner_info =  sig_info_query.openeuler_repo_owner.get(deps_info['detail_src_name'], default_info)
            if not package_owner_info:
                logger.warning("NO owner info for %s", deps_info['detail_src_name'])
            else:
                analyse_report_details[main_key].update(package_owner_info)

    @staticmethod
    def analyse_obsolete_deps(obsolete_rpm: str, unresolv_type: str):
        """
        
        Args:
        
        Returns:
        
        """
        
        # missing_provides.add(provides)
        src_name = Command().binary_source(obsolete_rpm)
        deps_info = dict()
        deps_info['unresolv_type'] = unresolv_type
        deps_info['detail'] = obsolete_rpm
        deps_info['detail_bin_name'] = obsolete_rpm
        deps_info['detail_src_name'] = src_name
        deps_info['require_request'] = 'NA'

        return deps_info

    @staticmethod
    def analyse_missing_deps(missing_require: str, unresolv_type: str):
        """
        
        Args:
        
        Returns:
        
        """
        
        deps_info = dict()
        missing_provides = set()
        missing_require = missing_require.strip()
        provides = ''
        bin_name = ''
        src_name = ''
        require_request = ''
        match_compare_operator = False
        for coop in COMPARE_OPERATORS:
            if coop in missing_require:
                logger.info(f"match {coop}")
                match_compare_operator = True
                co_splits = missing_require.split(coop)
                provides = co_splits[0].strip()
                require_request = coop + co_splits[1]
                logger.info(f"get provides: {provides}")
                break

        if not match_compare_operator:
            provides = missing_require
            require_request = 'NA'

        missing_provides.add(provides)
        bin_name, src_name = Command().provides_bin_source(provides)

        deps_info['unresolv_type'] = unresolv_type
        deps_info['details'] = provides
        deps_info['detail_bin_name'] = bin_name
        deps_info['detail_src_name'] = src_name
        deps_info['require_request'] = require_request

        logger.info(deps_info)

        return deps_info

    @classmethod
    def analyse_unresolvable(cls, all_unresolvable_info: dict, branch: str):
        """
        analyse unresolvable details, for example: 
        (1) conflict for providers of libdtkcore.so.2()(64bit) needed by dtkwm, 
        (2) (provider dtkcore2 is obsoleted by dtkcore)
        (3) nothing provides liboil-devel >= 0.3.6
        (4) conflict for providers of libtcnative-1-0 needed by netty-tcnative, 
        (5) (provider libtcnative-1-0 is in conflict with tomcatjss)
        (6) nothing provides (npm(eventemitter2) >= 6.4.5 with npm(eventemitter2) < 7) needed by nodejs-grunt
        (7) nothing provides libunistring.so.5()(64bit) needed by gnutls
        Args:
        
        Returns:
        
        """

        start = time.monotonic()
        same_need_by_record = set()
        unresolvable_report_details = dict()

        for unresolvable_info, influented_pkgs in all_unresolvable_info.items():
            logger.info("unresolvable_info: %s", unresolvable_info)
            missing_requires = ""
            unresolv_type = ""

            if ' needed by' in unresolvable_info:
                need_by_splits = unresolvable_info.split(' needed by')
                logger.info(need_by_splits)
                # 如果是重复的unresolvable信息，跳过
                if need_by_splits[0] in same_need_by_record:
                    logger.warning("%s already been analysed", need_by_splits[0])
                    continue
                same_need_by_record.add(need_by_splits[0])

                if 'nothing provides ' in need_by_splits[0]:
                    missing_requires = need_by_splits[0].replace('nothing provides ', '')
                    unresolv_type = "nothing_provides"
                elif 'conflict for providers of ' in need_by_splits[0]:
                    missing_requires = need_by_splits[0].replace('conflict for providers of ', '')
                    unresolv_type = "conflict_providers"
                else:
                    logger.info("unkonwn unresolvable info:%s", need_by_splits[0])
                    unresolv_type = "unknown"
                    missing_requires = need_by_splits[0]

            elif ' is obsoleted by ' in unresolvable_info:
                logger.info(unresolvable_info)
                unresolv_type = 'obsoleted_by'
                unresolvable_info = unresolvable_info.strip('\(\)') # 删除首尾的()
                unresolvable_info = unresolvable_info.replace('provider ', '')
                obsoleted_splits = unresolvable_info.split(' is obsoleted by ')
                deps_info = cls.analyse_obsolete_deps(obsoleted_splits[1], unresolv_type)
                cls._add_missing_deps_owner_info(deps_info, influented_pkgs, unresolvable_report_details)
                continue
            elif ' is in conflict with ' in unresolvable_info:
                logger.info(unresolvable_info)
                unresolv_type = 'conflict_with'
                unresolvable_info = unresolvable_info.strip('\(\)') # 删除首尾的()
                unresolvable_info = unresolvable_info.replace('provider ', '')
                conflict_splits = unresolvable_info.split(' is in conflict with ')
                deps_info = cls.analyse_obsolete_deps(conflict_splits[1], unresolv_type)
                cls._add_missing_deps_owner_info(deps_info, influented_pkgs, unresolvable_report_details)
                continue
            else:
                if 'nothing provides ' in unresolvable_info:
                    missing_requires = unresolvable_info.replace('nothing provides ', '')
                    unresolv_type = "nothing_provides"
                else:
                    logger.info("unkonwn unresolvable info:%s", unresolvable_info)
                    unresolv_type = "unknown"
                    missing_requires = unresolvable_info

            if ' or ' in missing_requires or ' with ' in missing_requires:
                logger.info(missing_requires)
                full_or_regular = re.compile(r'\(.*.\)')
                line = re.findall(full_or_regular, missing_requires)[0] # 匹配最外层的()内容 
                line = line.strip('\(\)') # 删除首尾的()
                unresolv_splits = line.split(' or ') # 分割为两个unresolvable信息
                unresolv_splits = line.split(' with ') # 分割为两个unresolvable信息
                logger.info(f"unresolv_splits: {unresolv_splits}")
                deps_info1 = cls.analyse_missing_deps(unresolv_splits[0], unresolv_type)
                cls._add_missing_deps_owner_info(deps_info1, influented_pkgs, unresolvable_report_details)
                deps_info2 = cls.analyse_missing_deps(unresolv_splits[1], unresolv_type)
                cls._add_missing_deps_owner_info(deps_info2, influented_pkgs, unresolvable_report_details)
            else:
                logger.info(missing_requires)
                deps_info = cls.analyse_missing_deps(missing_requires, unresolv_type)
                cls._add_missing_deps_owner_info(deps_info, influented_pkgs, unresolvable_report_details)

        used_time = time.monotonic() - start
        logger.info("used_time:%s", (used_time))

        return unresolvable_report_details